package com.yuexunit.auth.service;

import com.yuexunit.account.action.v2.model.Operation;
import com.yuexunit.account.action.v2.model.Role;
import com.yuexunit.auth.entity.AccountRoleEntity;
import com.yuexunit.auth.entity.OperationEntity;
import com.yuexunit.auth.entity.RoleEntity;
import com.yuexunit.auth.entity.RoleOperationEntity;
import com.yuexunit.core.exception.DuplicateException;
import com.yuexunit.core.exception.NoPermissionException;
import com.yuexunit.core.exception.NotFoundException;
import com.yuexunit.inject.annotation.Implements;
import com.yuexunit.persist.PersistUtil;
import com.yuexunit.persist.annotation.Transactional;
import com.yuexunit.util.DateUtil;

import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;

import java.util.ArrayList;
import java.util.List;


/**
 * @author wangjinshui
 */
@Implements(AuthorizationService.class)
public class AuthorizationServiceImpl implements AuthorizationService {

    @Override
    public List<Role> inquireRoleList(String appUuid, Long accountId) {
        List<Role> roleList = inquireRoleList(appUuid);
        List<Long> roleIdListOwned = inquireRoleIdList(appUuid, accountId);

        for (Role each : roleList) {
            if (roleIdListOwned.contains(each.getRoleId())) {
                each.setOwned(new Integer(1));
            }
        }
        return roleList;
    }

    @Override
    public Role inquireRole(Long roleId) {
        RoleEntity ens = PersistUtil.get(RoleEntity.class, roleId);
        if (ens == null) {
            return null;
        }
        return ens.toRole();
    }

    private List<Role> inquireRoleList(String appUuid) {
        DetachedCriteria cri = DetachedCriteria.forClass(RoleEntity.class);
        cri.add(Restrictions.eq("appUuid", appUuid));

        List<RoleEntity> roleEntityList = PersistUtil.get(cri);

        List<Role> result = new ArrayList<Role>();
        for (RoleEntity each : roleEntityList) {
            result.add(each.toRole());
        }

        return result;
    }

    private List<Long> inquireRoleIdList(String appUuid, Long accountId) {
        List<AccountRoleEntity> entityList = inquireAccountRoleEntityList(appUuid, accountId);

        List<Long> result = new ArrayList<Long>();
        for (AccountRoleEntity each : entityList) {
            result.add(each.getRoleId());
        }

        return result;
    }

    private List<AccountRoleEntity> inquireAccountRoleEntityList(String appUuid, Long accountId) {
        DetachedCriteria cri = DetachedCriteria.forClass(AccountRoleEntity.class);
        cri.add(Restrictions.eq("appUuid", appUuid))
                .add(Restrictions.eq("accountId", accountId));

        List<AccountRoleEntity> entityList = PersistUtil.get(cri);
        return entityList;
    }


    @Override
    @Transactional
    public void updateAccountRoles(String appUuid, Long accountId, List<Long> roleIdList) {
        List<AccountRoleEntity> entityList = inquireAccountRoleEntityList(appUuid, accountId);

        for (AccountRoleEntity each : entityList) {
            PersistUtil.delete(each);
        }

        for (Long eachRoleId : roleIdList) {
            PersistUtil.insert(new AccountRoleEntity(appUuid, accountId, eachRoleId));
        }
    }


    @Override
    public List<String> inquireOperationListOfAccount(String appUuid, Long accountId) {
        List<Long> roleIdListOwned = inquireRoleIdList(appUuid, accountId);

        List<RoleOperationEntity> entityList = inquireRoleOperationEntityList(roleIdListOwned);

        List<String> operationCodeList = new ArrayList<String>();
        for (RoleOperationEntity each : entityList) {
            operationCodeList.add(each.getOperationCode());
        }

        return operationCodeList;
    }


    @Override
    @Transactional
    public Long createRole(String appUuid, String roleName) {
        checkDuplicateRole(appUuid, roleName);

        RoleEntity entity = new RoleEntity(appUuid, roleName);
        PersistUtil.insert(entity);

        return entity.getRoleId();
    }

    private void checkDuplicateRole(String appUuid, String roleName) {
        DetachedCriteria cri = DetachedCriteria.forClass(RoleEntity.class);
        cri.add(Restrictions.eq("appUuid", appUuid))
                .add(Restrictions.eq("roleName", roleName))
        ;

        List<Role> entityList = PersistUtil.get(cri);
        if (entityList.size() >= 1) {
            throw new DuplicateException("角色已定义");
        }
    }

    @Override
    @Transactional
    public void updateRole(String appUuid, Long roleId, String roleName) {
        RoleEntity entity = inquireRoleEntity(appUuid, roleId);
        entity.setRoleName(roleName);
        entity.setCreateDate(DateUtil.now());

        PersistUtil.update(entity);
    }

    private RoleEntity inquireRoleEntity(String appUuid, Long roleId) {
        RoleEntity existed = PersistUtil.get(RoleEntity.class, roleId);

        if (existed == null) {
            throw new NotFoundException("角色不存在");
        }

        if (!existed.getAppUuid().equals(appUuid)) {
            throw new NoPermissionException("无权访问该角色");
        }

        return existed;
    }

    @Override
    @Transactional
    public void deleteRole(String appUuid, Long roleId) {
        RoleEntity entity = inquireRoleEntity(appUuid, roleId);

        PersistUtil.delete(entity);
    }

    @Override
    public List<Operation> inquireOperationList(String appUuid, Long roleId) {
        List<Operation> operationList = inquireOperationList(appUuid);
        List<String> operationCodeListOwned = inquireOperationCodeList(roleId);

        for (Operation each : operationList) {
            if (operationCodeListOwned.contains(each.getOperationCode())) {
                each.setOwned(new Integer(1));
            }
        }
        return operationList;
    }

    private List<Operation> inquireOperationList(String appUuid) {
        DetachedCriteria cri = DetachedCriteria.forClass(OperationEntity.class);
        cri.add(Restrictions.eq("appUuid", appUuid));

        List<OperationEntity> entityList = PersistUtil.get(cri);

        List<Operation> result = new ArrayList<Operation>();
        for (OperationEntity each : entityList) {
            result.add(each.toOperation());
        }

        return result;
    }

    private List<String> inquireOperationCodeList(Long roleId) {
        List<RoleOperationEntity> entityList = inquireRoleOperationEntityList(roleId);

        ArrayList<String> result = new ArrayList<String>();
        for (RoleOperationEntity each : entityList) {
            result.add(each.getOperationCode());
        }

        return result;
    }

    private List<RoleOperationEntity> inquireRoleOperationEntityList(Long roleId) {
        ArrayList<Long> roleIdList = new ArrayList<Long>();
        roleIdList.add(roleId);

        return inquireRoleOperationEntityList(roleIdList);
    }


    private List<RoleOperationEntity> inquireRoleOperationEntityList(List<Long> roleIdList) {
        if (roleIdList == null || roleIdList.isEmpty()) {
            return new ArrayList<RoleOperationEntity>();
        }

        DetachedCriteria cri = DetachedCriteria.forClass(RoleOperationEntity.class);
        cri.add(Restrictions.in("roleId", roleIdList));

        List<RoleOperationEntity> entityList = PersistUtil.get(cri);
        return entityList;
    }

    @Override
    @Transactional
    public void updateRoleOperations(String appUuid, Long roleId, List<String> operationCodeList) {
        List<RoleOperationEntity> entityList = inquireRoleOperationEntityList(roleId);

        for (RoleOperationEntity each : entityList) {
            PersistUtil.delete(each);
        }

        for (String operationCode : operationCodeList) {
            PersistUtil.insert(new RoleOperationEntity(roleId, operationCode));
        }
    }
}
